﻿using System;
using System.Collections.Generic;
using Netbits.Common.Threading;

namespace Netbits.Common.Collections
{
	/// <summary>
	/// This class represent a dictionary that was locked with the lock-class. 
	/// </summary>
	/// <typeparam name="TKey">TypeOf KEY</typeparam>
	/// <typeparam name="TValue">TypeOf Value</typeparam>
	public sealed class LockedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDisposable
	{
		#region Private Variables

		/// <summary>
		/// The dictionary to be locked.
		/// </summary>
		readonly Dictionary<TKey, TValue> _Inner;
		/// <summary>
		/// The lock instance.
		/// </summary>
		Lock _Lock;

		bool _Disposed;

		#endregion

		#region Constructor

		/// <summary>
		/// Create a locked dictionary instance.
		/// </summary>
		public LockedDictionary()
		{
			_Inner = new Dictionary<TKey, TValue>();
			_Lock = new Lock();
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Get a collection of all keys in the dictionary.
		/// </summary>
		public ICollection<TKey> Keys
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _Inner.Keys;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
		}

		/// <summary>
		/// Get a collection of all values in the dictionary
		/// </summary>
		public ICollection<TValue> Values
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _Inner.Values;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
		}

		/// <summary>
		/// Get the TValue associated with the specified key or set this.
		/// </summary>
		/// <param name="Key_">The key of the value</param>
		/// <returns>The TValue associated with the specified key, otherwise default(TValue)</returns>
		public TValue this[TKey Key_]
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();

					if( _Inner.ContainsKey( Key_ ) )
						return _Inner[Key_];
					return default( TValue );
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					_Inner[Key_] = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		/// <summary>
		/// Get the number of key/value pairs in the dictionary.
		/// </summary>
		public int Count
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _Inner.Count;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
		}

		/// <summary>
		/// Returns if the dictionary is read-only. For this implementation always returns false
		/// </summary>
		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Returns the sync (lock) object
		/// </summary>
		public Lock SyncObject
		{
			get
			{
				return _Lock;
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Add the specified key/value to the dictionary.
		/// </summary>
		/// <param name="Key_">Key_ of the item to add</param>
		/// <param name="Value_">Value_ of the item to add.</param>
		public void Add( TKey Key_, TValue Value_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Add( Key_, Value_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Add the specified KeyValuePair to the dictionary
		/// </summary>
		/// <param name="Item_">Item_ to add</param>
		public void Add( KeyValuePair<TKey, TValue> Item_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Add( Item_.Key, Item_.Value );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Determines whether the dictionary contains the specified key.
		/// </summary>
		/// <param name="Key_">The Key_ locate in the dictionary</param>
		/// <returns>true if the dictionary contains an item with the specified key, otherwise false</returns>
		public bool ContainsKey( TKey Key_ )
		{
			try
			{
				_Lock.EnterReadLock();
				return _Inner.ContainsKey( Key_ );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Determines whether the dictionary contains the specified KeyValuePair
		/// </summary>
		/// <param name="Item_">The Item_ locate in the dictionary</param>
		/// <returns>true if the dictionary contains the item, otherwise false</returns>
		public bool Contains( KeyValuePair<TKey, TValue> Item_ )
		{
			try
			{
				_Lock.EnterReadLock();
				return _Inner.ContainsKey( Item_.Key );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Remove the Item with the specified Key_ from the dictionary.
		/// </summary>
		/// <param name="Key_">The Key_ of the item to remove</param>
		/// <returns>true if the item is successfully removed from the dictionary, otherwise false</returns>
		public bool Remove( TKey Key_ )
		{
			try
			{
				_Lock.EnterWriteLock();

				if( !_Inner.ContainsKey( Key_ ) )
					return false;
				return _Inner.Remove( Key_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Remove the KeyValuePair from the dictionary.
		/// </summary>
		/// <param name="Item_">The KeyValuePair to remove</param>
		/// <returns>true if the item is successfully removed, otherwise false</returns>
		public bool Remove( KeyValuePair<TKey, TValue> Item_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				return _Inner.Remove( Item_.Key );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Get the item associated with the specified key.
		/// </summary>
		/// <param name="Key_">The key of the item to get.</param>
		/// <param name="Value_">When found, contains the Value_ associated with the specified Key_, otherwise the default(TValue)</param>
		/// <returns></returns>
		public bool TryGetValue( TKey Key_, out TValue Value_ )
		{
			try
			{
				_Lock.EnterReadLock();
				return _Inner.TryGetValue( Key_, out Value_ );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Remove all KeyValuePair items of the dictionary.
		/// </summary>
		public void Clear()
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Clear();
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Copies the items of the dictionary to an array of type KeyValuePair.
		/// </summary>
		/// <param name="Array_">The one-dimensional array that is the destination of the items.</param>
		/// <param name="ArrayIndex_">The zero-based index of array at witch copying begins.</param>
		public void CopyTo( KeyValuePair<TKey, TValue>[] Array_, int ArrayIndex_ )
		{
			try
			{
				_Lock.EnterReadLock();
				int index = ArrayIndex_;
				int maxItems = Array_.Length - index;
				foreach( KeyValuePair<TKey, TValue> item in _Inner )
				{
					if( maxItems == 0 )
						break;

					Array_[index++] = item;
					--maxItems;
				}
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Returns a locked enumerator that iterates through the dictionary
		/// </summary>
		/// <returns>The locked enumerator</returns>
		public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
		{
			return new LockedEnumerator<KeyValuePair<TKey, TValue>>( _Inner, _Lock );
		}

		/// <summary>
		/// Returns a locked enumerator that iterates through the dictionary
		/// </summary>
		/// <returns>The locked enumerator</returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return new LockedEnumerator<KeyValuePair<TKey, TValue>>( _Inner, _Lock );
		}

		#endregion

		#region IDisposable

		/// <summary>
		/// Dispose LockedDictionary Resources
		/// </summary>
		public void Dispose()
		{
			if( !_Disposed )
			{
				if( _Inner != null )
					_Inner.Clear();

				if( _Lock != null )
				{
					_Lock.Dispose();
					_Lock = null;
				}

				_Disposed = true;
			}

			GC.SuppressFinalize( this );
		}

		#endregion
	}
}

